package cn.edu.usst.cs;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.Future;

/**
 * MagicSquareFilter 类用于创建一个特定大小的幻方，并筛选出满足条件的数字。
 * 幻方是一个二维数组，其中所有行、列及对角线上的数字之和相等。
 * 该类使用并发处理来提高筛选效率。
 */
public class MagicSquareFilter {

    /** 定义幻方的大小 */
    private static final int SIZE = 9;

    /** 存储幻方的二维数组 */
    private static final int[][] MAGIC_SQUARE;

    /**
     * 打印幻方到控制台。
     */
    static void printMagicSquare() {
        for (int r = 0; r < SIZE; r++) {
            for (int c = 0; c < SIZE; c++) {
                System.out.printf("%2d,", MAGIC_SQUARE[r][c]);
            }
            System.out.println();
        }
    }

    /**
     * 静态初始化块，用于构建幻方。
     * 幻方是按照一定的规则填充的，这里使用的是奇数阶幻方的填充方法。
     */
    static {
        int[][] square = new int[SIZE][SIZE];

        int row_ini = 0;
        int col_ini = SIZE / 2;
        for (int row = row_ini, col = col_ini, value = 0; ; ) {
            square[row][col] = ++value;
            row = (row + SIZE - 1) % SIZE;
            if (row == row_ini) {
                row = (row + 2) % SIZE;
                row_ini = row;
            } else {
                col = (col + 1) % SIZE;
            }
            if (value == SIZE * SIZE) break;
        }
        MAGIC_SQUARE = square;
    }

    /**
     * 计算给定列的所有元素之和。
     *
     * @param col 要计算的列号
     * @return 列的总和
     */
    private static int colSum(int col) {
        int sum = 0;
        for (int[] rData : MAGIC_SQUARE) {
            sum += rData[col];
        }
        return sum;
    }

    /**
     * 计算给定行的所有元素之和。
     *
     * @param row 要计算的行号
     * @return 行的总和
     */
    private static int rowSum(int row) {
        int sum = 0;
        int[] rData = MAGIC_SQUARE[row];
        for (int rDatum : rData) {
            sum += rDatum;
        }
        return sum;
    }

    /**
     * 计算主对角线或副对角线上所有元素之和。
     *
     * @param fromTopLeft 如果为true，则计算从左上到右下的对角线；否则计算从右上到左下的对角线。
     * @return 对角线的总和
     */
    public static int diagonalSum(boolean fromTopLeft) {
        int sum = 0;
        for (int row = 0; row < SIZE; row++) {
            int col = fromTopLeft ? row : (SIZE - 1 - row);
            sum += MAGIC_SQUARE[col][row];
        }
        return sum;
    }

    /**
     * 结果类，用于存储满足条件的数字及其位置。
     */
    private static class Result {
        int r, c;
        int number;
        int digitSum;

        /**
         * 构造一个新的结果对象。
         *
         * @param r 行号
         * @param c 列号
         * @param digitSum 数字的各位数之和
         */
        Result(int r, int c, int digitSum) {
            this.r = r;
            this.c = c;
            this.number = MagicSquareFilter.MAGIC_SQUARE[r][c];
            this.digitSum = digitSum;
        }

        @Override
        public String toString() {
            return "发现数字 " + number + "\t位于 " + r + "，" + c;
        }
    }

    /**
     * 行结果处理器，实现 Callable 接口，用于并发地处理每一行的数据。
     */
    private record RowResultProcessor(int row) implements Callable<List<Result>> {

        @Override
        public List<Result> call() {
            List<Result> rowResults = new ArrayList<>();
            for (int col = 0; col < SIZE; col++) {
                int number = MAGIC_SQUARE[row][col];
                if (isIn45DegreeRange(row, col) && number % 7 == 0) {
                    rowResults.add(new Result(row, col, calculateDigitSum(number)));
                }
            }
            return rowResults;
        }
    }

    /**
     * 主函数，用于启动程序并执行主要逻辑。
     *
     * @param args 命令行参数（未使用）
     */
    public static void main(String[] args) {
        ForkJoinPool forkJoinPool = new ForkJoinPool();
        printMagicSquare();

        try {
            var start=System.currentTimeMillis();
            List<Future<List<Result>>> futures = new ArrayList<>();
            for (int row = 0; row < SIZE; row++) {
                futures.add(forkJoinPool.submit(new RowResultProcessor(row)));
            }

            List<Result> results = new ArrayList<>();
            for (Future<List<Result>> future : futures) {
                results.addAll(future.get());
            }

            int sum = 0;
            for (Result result : results) {
                sum += result.number;
            }
            System.out.println("用时"+(System.currentTimeMillis()-start)+"毫秒");

            results.forEach(System.out::println);
            System.out.println("得和: " + sum);

        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        } finally {
            forkJoinPool.shutdown();
        }
    }

    /**
     * 检查给定的位置是否在中心的45度范围内。
     *
     * @param row 行号
     * @param col 列号
     * @return 如果位置在45度范围内则返回true，否则返回false
     */
    private static boolean isIn45DegreeRange(int row, int col) {
        if (row > col) return false;
        return row + col >= SIZE - 1;
    }

    /**
     * 计算给定数字的各位数之和。
     *
     * @param number 要计算的数字
     * @return 数字的各位数之和
     */
    private static int calculateDigitSum(int number) {
        int sum = 0;
        while (number > 0) {
            sum += number % 10;
            number /= 10;
        }
        return sum;
    }
}